package io.starteos.wallet.utils.eos;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import io.starteos.wallet.utils.eos.bean.Account;
import io.starteos.wallet.utils.eos.bean.Accounts;
import io.starteos.wallet.utils.eos.bean.Block;
import io.starteos.wallet.utils.eos.bean.ChainInfo;
import io.starteos.wallet.utils.eos.bean.Producers;
import io.starteos.wallet.utils.eos.bean.TableRows;
import io.starteos.wallet.utils.eos.bean.TableRowsReq;
import io.starteos.wallet.utils.eos.bean.Transaction;
import io.starteos.wallet.utils.eos.crypto.ec.EosPrivateKey;
import io.starteos.wallet.utils.eos.crypto.ec.EosPublicKey;
import io.starteos.wallet.utils.eos.data.remote.model.chain.Action;
import io.starteos.wallet.utils.eos.data.remote.model.chain.PackedTransaction;
import io.starteos.wallet.utils.eos.data.remote.model.chain.SignedTransaction;
import io.starteos.wallet.utils.eos.data.remote.model.types.EosAddCoins;
import io.starteos.wallet.utils.eos.data.remote.model.types.EosBuyToken;
import io.starteos.wallet.utils.eos.data.remote.model.types.EosEraseorder;
import io.starteos.wallet.utils.eos.data.remote.model.types.EosTransfer;
import io.starteos.wallet.utils.eos.data.remote.model.types.EosUndelegatebw;
import io.starteos.wallet.utils.eos.data.remote.model.types.Eosdelegatebw;
import io.starteos.wallet.utils.eos.data.remote.model.types.TypeChainId;
import io.starteos.wallet.utils.eos.data.remote.model.types.TypeKeyWeight;
import io.starteos.wallet.utils.eos.data.remote.model.types.TypePublicKey;


public class Rpc {

	private final RpcService rpcService;
	
	private String baseUrl;

	SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

	public Rpc(String baseUrl) {
		this.baseUrl = baseUrl;
		dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
		rpcService = Generator.createService(RpcService.class, baseUrl);
	}
	/**
	 * 获得table数据
	 * 
	 * @param req
	 * @return
	 */
	public TableRows getTableRows(TableRowsReq req) {
		return Generator.executeSync(rpcService.getTableRows(req));
	}
	/**
	 * 获得链信息
	 * 
	 * @return
	 */
	public ChainInfo getChainInfo() {
		return Generator.executeSync(rpcService.getChainInfo());
	}

	/**
	 * 获得区块信息
	 * 
	 * @param blockNumberOrId
	 *            区块ID或者高度
	 * @return
	 */
	public Block getBlock(String blockNumberOrId) {
		return Generator.executeSync(rpcService.getBlock(Collections.singletonMap("block_num_or_id", blockNumberOrId)));
	}
	/**
	 * 获得账户信息
	 * 
	 * @param account
	 *            账户名称
	 * @return
	 */
	public Account getAccount(String account) {
		return Generator.executeSync(rpcService.getAccount(Collections.singletonMap("account_name", account)));
	}

	/**
	 * 发送请求
	 * 
	 * @param compression
	 *            压缩
	 * @param pushTransaction
	 *            交易
	 * @param signatures
	 *            签名
	 * @return
	 * @throws Exception
	 */
	public Transaction pushTransaction(String str) throws Exception {
		return Generator.executeSync(rpcService.pushTransaction(str));
	}
	
	public String addCoins(String pk, String contractAccount, String account, String coins) throws Exception{
		Gson gson = new Gson();
		EosPrivateKey privateKey = new EosPrivateKey(pk);
		List<TypeKeyWeight> keyWeights = new ArrayList<TypeKeyWeight>();
		keyWeights.add(new TypeKeyWeight(new TypePublicKey(new EosPublicKey(privateKey.getPublicKey().toString())), (short)1));
		EosAddCoins eosAddCoins = new EosAddCoins(account, coins);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action(contractAccount,eosAddCoins.getActionName());
		action.setData(eosAddCoins.getAsHex());
		action.setAuthorization(new String[] {contractAccount+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	public String Undelegatebw(String prk, String from, String receiver, String netAsset, String cpuAsset) throws Exception{
		Gson gson = new Gson();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		List<TypeKeyWeight> keyWeights = new ArrayList<TypeKeyWeight>();
		keyWeights.add(new TypeKeyWeight(new TypePublicKey(new EosPublicKey(privateKey.getPublicKey().toString())), (short)1));
		EosUndelegatebw undelegatebw = new EosUndelegatebw(from, receiver, netAsset, cpuAsset);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("eosio",undelegatebw.getActionName());
		action.setData(undelegatebw.getAsHex());
		action.setAuthorization(new String[] {from+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	
	public String eraseorder(String prk, String name, String ord_time) throws Exception{
		Gson gson = new Gson();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		List<TypeKeyWeight> keyWeights = new ArrayList<TypeKeyWeight>();
		keyWeights.add(new TypeKeyWeight(new TypePublicKey(new EosPublicKey(privateKey.getPublicKey().toString())), (short)1));
		EosEraseorder undelegatebw = new EosEraseorder(name, ord_time);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("market.bank",undelegatebw.getActionName());
		action.setData(undelegatebw.getAsHex());
		action.setAuthorization(new String[] {"market.bank"+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	
	public String delegateByBank(String prk, String name, String net, String cpu, String days) throws Exception {
		Gson gson = new Gson();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		List<TypeKeyWeight> keyWeights = new ArrayList<TypeKeyWeight>();
		keyWeights.add(new TypeKeyWeight(new TypePublicKey(new EosPublicKey(privateKey.getPublicKey().toString())), (short)1));
		Eosdelegatebw  undelegatebw = new Eosdelegatebw(name, net, cpu, days);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("market.bank",undelegatebw.getActionName());
		action.setData(undelegatebw.getAsHex());
		action.setAuthorization(new String[] {"market.bank"+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	
	public Accounts getKeyAccounts(String puk) throws Exception{
		return Generator.executeSync(rpcService.getKeyAccounts(Collections.singletonMap("public_key", puk)));
	}
	
	public Producers getProducers(String limit, String beginName, String json) throws Exception{
		Map<String,String> map = new HashMap<String, String>();
		map.put("lower_bound", beginName);
		map.put("limit", limit);
		map.put("json", json);
		return Generator.executeSync(rpcService.getProducers(map));
	}
	
	public String transfer(String prk, String from, String to, String quantity, String memo) throws Exception{
		Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GsonEosType()).serializeNulls().excludeFieldsWithoutExposeAnnotation().create();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		EosTransfer  transfer = new EosTransfer(from, to, quantity, memo);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("eosio.token",transfer.getActionName());
		action.setData(transfer.getAsHex());
		action.setAuthorization(new String[] {from+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	
	public String buyToken(String prk, String buyer, String buyToken, String quantity, String type) throws Exception{
		Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GsonEosType()).serializeNulls().excludeFieldsWithoutExposeAnnotation().create();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		EosBuyToken  transfer = new EosBuyToken(buyer, buyToken, quantity, type);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("resourcetest",transfer.getActionName());
		action.setData(transfer.getAsHex());
		action.setAuthorization(new String[] {"resourcetest"+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
	
	public String sellToken(String prk, String from, String to, String quantity, String memo) throws Exception{
		Gson gson = new GsonBuilder().registerTypeAdapterFactory(new GsonEosType()).serializeNulls().excludeFieldsWithoutExposeAnnotation().create();
		EosPrivateKey privateKey = new EosPrivateKey(prk);
		EosTransfer  transfer = new EosTransfer(from, to, quantity, memo);
		SignedTransaction txn = new SignedTransaction();
		Action action = new Action("eosio.token",transfer.getActionName());
		action.setData(transfer.getAsHex());
		action.setAuthorization(new String[] {from+"@active"});
		txn.addAction(action);
		//=======================================================
		ChainInfo info = getChainInfo();
		txn.setReferenceBlock(info.getHeadBlockId());
		txn.putSignatures(new ArrayList<String>());//
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		sf.setTimeZone(TimeZone.getTimeZone("UTC"));
		txn.setExpiration(sf.format(info.getHeadBlockTime()).toString());
		txn.sign(privateKey, new TypeChainId(info.getChainId()));
		PackedTransaction packedTransaction = new PackedTransaction(txn);
		String request = gson.toJson(packedTransaction);
		return HttpUtils.post(baseUrl+"/v1/chain/push_transaction", request);
	}
}
